package edu.hawaii.myisern.model;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.commandline.MyIsernXmlLoader;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.researchers.jaxb.Researcher;

/**
 * Implements a singleton MyISERN instance.
 * To prevent multi-threaded access from corrupting MyISERN, all operations on the
 * singleton MyIsernModel instance are synchronized.
 *
 * @author Jon Lao
 * @author Jared Sunouchi
 * @author Chiao-Fen Zielinski-Liu
 */
public class MyIsernModel {
  /** The singleton MyIsernModel instance. */
  private static MyIsernModel theInstance = new MyIsernModel();
  
  /** The XML loader. */
  private MyIsernXmlLoader myIsernXml;
   
  
  /** Private constructor used to create a single instance of MyISERN. */
  private MyIsernModel() {
    try {
      this.myIsernXml = new MyIsernXmlLoader();
    }
    catch (Exception error) {
      // TODO Auto-generated catch block
      error.printStackTrace();
    }
  }
  
  /**
   * Sorts the researchers by name.
   */
  public void sortResearchers() {
    List<Researcher> rNames = myIsernXml.getResearchers().getResearcher();
    
    List<String> names = new ArrayList<String>();
    for (Researcher res : rNames) {
      names.add(res.getName());
    }
    
    Collections.sort(names);
    List<Researcher> sortedResearchers = new ArrayList<Researcher>();
    for (String name : names) {
      for (Researcher res : rNames) {
        if (name.equals(res.getName())) {
          sortedResearchers.add(res);
        }
      }
    }
    myIsernXml.getResearchers().getResearcher().clear();
    myIsernXml.getResearchers().getResearcher().addAll(sortedResearchers);
  }
  
  /**
   * Sorts the organizations by name.
   */
  public void sortOrganizations() {
    List<Organization> oNames = myIsernXml.getOrganizations().getOrganization();
    
    List<String> names = new ArrayList<String>();
    for (Organization org : oNames) {
      names.add(org.getName());
    }
    
    Collections.sort(names);
    List<Organization> sortedOrganizations = new ArrayList<Organization>();
    for (String name : names) {
      for (Organization org : oNames) {
        if (name.equals(org.getName())) {
          sortedOrganizations.add(org);
        }
      }
    }
    myIsernXml.getOrganizations().getOrganization().clear();
    myIsernXml.getOrganizations().getOrganization().addAll(sortedOrganizations);
  }
  
  /**
   * Sorts the collaborations by name.
   */
  public void sortCollaborations() {
    List<Collaboration> cNames = myIsernXml.getCollaborations().getCollaboration();
    
    List<String> names = new ArrayList<String>();
    for (Collaboration collab : cNames) {
      names.add(collab.getName());
    }
    
    Collections.sort(names);
    List<Collaboration> sortedCollaborations = new ArrayList<Collaboration>();
    for (String name : names ) {
      for (Collaboration collab : cNames) {
        if (name.equals(collab.getName())) {
          sortedCollaborations.add(collab);
        }
      }
    }
    myIsernXml.getCollaborations().getCollaboration().clear();
    myIsernXml.getCollaborations().getCollaboration().addAll(sortedCollaborations);
  }
  
  /**
   * Get the single instance of the MyIsernModel object.
   *
   * @return A MyIsernModel.
   */
  public static synchronized MyIsernModel getInstance() {
    return MyIsernModel.theInstance;
  }
  
  /**
   * Creates and returns a list object representation of a string.
   * 
   * @param listString  The string that will be converted to a list.
   * @return The list object version of the string.
   */
  public synchronized List<String> createList(String listString) {
    List<String> list = new ArrayList<String>();
    if (listString == null) {
      return list;
    }
    
    String copy = listString;
    String part = "";

    while (copy.contains(",")) {
      int stopIndex = copy.indexOf(',');
      part = copy.substring(0, stopIndex);
      copy = copy.substring(stopIndex + 1);
      list.add(part.trim());
    }
    list.add(copy);
    
    return list;
  }
  
  /**
   * Generates a string representation of a list.
   * 
   * @param listElements The list of elements that will be turned into a string.
   * @return The string representation of the list.
   */
  public synchronized String createListString(List<String> listElements) {
    int listSize = listElements.size();
    int listCount = 1;
    StringBuffer listString = new StringBuffer();
    for (String element : listElements) {
      if (listCount < listSize) {
        element += ", ";
      }
      listString.append(element);
      listCount++;
    }
    
    return listString.toString();
  }
  
  /**
   * Gets the iterator attribute of the researcher list.
   *
   * @return The researcher list iterator value.
   */
  public synchronized List<Researcher> getResearchers() {
    return myIsernXml.getResearchers().getResearcher();
  }
  
  /**
   * Adds a researcher.
   * 
   * @param newResearcher The research that will be added.
   * @throws Exception If problems occur.
   */
  public synchronized void addResearcher(Researcher newResearcher) throws Exception {
    myIsernXml.getResearchers().getResearcher().add(newResearcher);
    String newResearchersXml = myIsernXml.getResearchersXml();
    File researchersFile = new File(myIsernXml.getRsearchersFilePath());
    FileOutputStream fos = new FileOutputStream(researchersFile);
    fos.write(newResearchersXml.getBytes());
  }
  
  /**
   * Edits a researcher.
   * 
   * @param editResearcher The researcher that will be edited.
   * @param count The number that will be calculated.
   * @throws Exception If problems occur.
   */
  public synchronized void editResearcher(int count, Researcher editResearcher)
      throws Exception {
    myIsernXml.getResearchers().getResearcher().set(count, editResearcher);
    String newResearchersXml = myIsernXml.getResearchersXml();
    File researchersFile = new File(myIsernXml.getRsearchersFilePath());
    FileOutputStream fos = new FileOutputStream(researchersFile);
    fos.write(newResearchersXml.getBytes());
  }
  
  /**
   * Removes the researcher.
   * 
   * @param name The name of the researcher that will be removed.
   * @throws Exception If problems occur.
   */
  public synchronized void removeResearcher(String name) throws Exception {
    Researcher removeResearcher = new Researcher();
    List<Researcher> list = myIsernXml.getResearchers().getResearcher();
    for (Researcher researcher : list) {
      if (name.equals(researcher.getName())) {
        removeResearcher = researcher;
      }
    }
    myIsernXml.getResearchers().getResearcher().remove(removeResearcher);
    String newResearchersXml = myIsernXml.getResearchersXml();
    File researchersFile = new File(myIsernXml.getRsearchersFilePath());
    FileOutputStream fos = new FileOutputStream(researchersFile);
    fos.write(newResearchersXml.getBytes());
  }
  
  /**
   * Gets the iterator attribute of the organization list.
   *
   * @return The organization list iterator value.
   */
  public synchronized List<Organization> getOrganizations() {
    return myIsernXml.getOrganizations().getOrganization();
  }
  
  /**
   * Adds a organization.
   * 
   * @param newOrganization The organization that will be added.
   * @throws Exception If problems occur.
   */
  public synchronized void addOrganization(Organization newOrganization) throws Exception {
    myIsernXml.getOrganizations().getOrganization().add(newOrganization);
    String newOrganizationsXml = myIsernXml.getOrganizationsXml();
    File organizationsFile = new File(myIsernXml.getOrganizationsFilePath());
    FileOutputStream fos = new FileOutputStream(organizationsFile);
    fos.write(newOrganizationsXml.getBytes());
  }
  
  /**
   * Edits a organization.
   * 
   * @param editOrganization The organization that will be edited.
   * @param count The number that will be calculated.
   * @throws Exception If problems occur.
   */
  public synchronized void editOrganization(int count, Organization editOrganization)
      throws Exception {
    myIsernXml.getOrganizations().getOrganization().set(count, editOrganization);
    String newOrganizationsXml = myIsernXml.getOrganizationsXml();
    File organizationsFile = new File(myIsernXml.getOrganizationsFilePath());
    FileOutputStream fos = new FileOutputStream(organizationsFile);
    fos.write(newOrganizationsXml.getBytes());
  }
  
  /**
   * Removes the organization.
   * 
   * @param name The name of the organization that will be removed.
   * @throws Exception If problems occur.
   */
  public synchronized void removeOrganization(String name) throws Exception {
    Organization removeOrganization = new Organization();
    List<Organization> list = myIsernXml.getOrganizations().getOrganization();
    for (Organization organization : list) {
      if (name.equals(organization.getName())) {
        removeOrganization = organization;
      }
    }
    myIsernXml.getOrganizations().getOrganization().remove(removeOrganization);
    String newOrganizationsXml = myIsernXml.getOrganizationsXml();
    File organizationsFile = new File(myIsernXml.getOrganizationsFilePath());
    FileOutputStream fos = new FileOutputStream(organizationsFile);
    fos.write(newOrganizationsXml.getBytes());
  }
  
  /**
   * Gets the iterator attribute of the collaboration list.
   *
   * @return The collaboration list iterator value.
   */
  public synchronized List<Collaboration> getCollaborations() {
    return myIsernXml.getCollaborations().getCollaboration();
  }
  
  /**
   * Adds a collaboration
   * 
   * @param newCollaboration The collaboration that will be added.
   * @throws Exception If problems occur.
   */
  public synchronized void addCollaboration(Collaboration newCollaboration) throws Exception {
    myIsernXml.getCollaborations().getCollaboration().add(newCollaboration);
    String newCollaborationsXml = myIsernXml.getCollaborationsXml();
    File collaborationsFile = new File(myIsernXml.getCollaborationsFilePath());
    FileOutputStream fos = new FileOutputStream(collaborationsFile);
    fos.write(newCollaborationsXml.getBytes());
  }
  
  /**
   * Edits a collaboration.
   * 
   * @param editCollaboration The collaboration that will be edited.
   * @param count The number that will be calculated.
   * @throws Exception If problems occur.
   */
  public synchronized void editCollaboration(int count, Collaboration editCollaboration)
      throws Exception {
    myIsernXml.getCollaborations().getCollaboration().set(count, editCollaboration);
    String newCollaborationsXml = myIsernXml.getCollaborationsXml();
    File collaborationsFile = new File(myIsernXml.getCollaborationsFilePath());
    FileOutputStream fos = new FileOutputStream(collaborationsFile);
    fos.write(newCollaborationsXml.getBytes());
  }
  
  /**
   * Removes the collaboration.
   * 
   * @param name The name of the collaboration that will be removed.
   * @throws Exception If problems occur.
   */
  public synchronized void removeCollaboration(String name) throws Exception {
    Collaboration removeCollaboration = new Collaboration();
    List<Collaboration> list = myIsernXml.getCollaborations().getCollaboration();
    for (Collaboration collaboration : list) {
      if (name.equals(collaboration.getName())) {
        removeCollaboration = collaboration;
      }
    }
    myIsernXml.getCollaborations().getCollaboration().remove(removeCollaboration);
    String newCollaborationsXml = myIsernXml.getCollaborationsXml();
    File collaborationsFile = new File(myIsernXml.getCollaborationsFilePath());
    FileOutputStream fos = new FileOutputStream(collaborationsFile);
    fos.write(newCollaborationsXml.getBytes());
  }
  
  /**
   * Gets the sanity check results list from XmlLoader so that the ActionBean
   * can grab it.
   * @return the Sanity Check results list.
   */
  public synchronized List<String> getSanityCheckResultsList() {
    this.myIsernXml.sanityCheck();
    return this.myIsernXml.getSanityCheckResults();
  }
  
  
}
